.386
.model flat, stdcall
option casemap :none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc
include \masm32\include\fpu.inc

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\fpu.lib


PrintAndRead macro text:REQ, buffer:REQ
    invoke StdOut, text
    invoke StdIn, ADDR buffer, LengthOf buffer
endm


PrintSt0 macro
    invoke FpuFLtoA, 0, 10, addr buf, SRC1_FPU or SRC2_DIMM
    invoke StdOut, addr buf
endm


.const
    two dq 2.0
    four dt 4.0
    NewLine db 10, 13, 0
    EnterA db "Enter a: ", 0
    EnterB db "Enter b: ", 0
    EnterC db "Enter c: ", 0
    X1EqTo db "x1 = ", 0
    X2EqTo db "x2 = ", 0
    ConversionError db "Cannot convert between string and real", 10, 13, 0
    NotAQE db "Not a quadratic equation", 10, 13, 0
    NegD db "Negative Discriminant", 10, 13, 0

.data?
    a dt ?
    b dt ?
    d dt ?

.data
    buf db 64 dup (0)

 
.code
ReadInput proc
    PrintAndRead ADDR EnterA, buf
    invoke FpuAtoFL, ADDR buf, ADDR a, DEST_FPU     ; pushing to the FPU stack
    cmp eax, 0
    je conv_err

    PrintAndRead ADDR EnterB, buf
    invoke FpuAtoFL, ADDR buf, ADDR b, DEST_FPU     ; pushing to the FPU stack
    cmp eax, 0
    je conv_err

    PrintAndRead ADDR EnterC, buf
    invoke FpuAtoFL, ADDR buf, ADDR d, DEST_FPU     ; pushing to the FPU stack
    cmp eax, 0
    je conv_err
    
    jmp end_read

    conv_err:
        invoke StdOut, ADDR ConversionError
        invoke ExitProcess, 0
        
    end_read:
        ret
ReadInput endp


CheckInput proc
    push eax

    fld st(2)       ; pushing a copy of the 'a' value to the fpu stack for testing it
    ftst            ; compare ST(0) val to 0
    fstsw ax        ; copy the status word to ax
    fwait
    sahf            ; transfer ah to cpu flags
    fstp st         ; popping a copy of the 'a' value from the fpu stack 
    pop eax
    jnz end_check

    invoke StdOut, ADDR NotAQE
    invoke ExitProcess, 0
    
    end_check:
        ret
CheckInput endp


CalculateD proc
    fld st(1)           ; b
    fmul st, st         ; * b
    fld four            ; 4
    fmul st, st(4)      ; * a
    fmul st, st(2)      ; * c
    fsubp st(1), st
    ret
CalculateD endp


CheckD proc
    push eax
    ftst
    fstsw ax
    fwait
    sahf                ; transfer ah to the cpu flags
    pop eax
    jnb end_checkd      ; CF is not set (st is non-negative)

    invoke StdOut, ADDR NegD
    invoke ExitProcess, 0

    end_checkd:
        ret
CheckD endp


CalculateRoots proc
    fsqrt       ; sqrt of descr
    
    fld st(2)
    fchs
    fsub st, st(1)
    fdiv two
    fdiv st, st(4)

    fld st(3)
    fchs
    fadd st, st(2)
    fdiv two
    fdiv st, st(5)    

    ret
CalculateRoots endp


start:
    invoke ReadInput        ; +3 to the FPU stack
    invoke CheckInput
    invoke CalculateD       ; +1 to the FPU stack
    invoke CheckD
    invoke CalculateRoots   ; +2 to the FPU stack
    invoke StdOut, addr X1EqTo
    PrintSt0
    invoke StdOut, addr NewLine
    invoke StdOut, addr X2EqTo
    fstp st                 ; -1 to the FPU stack
    PrintSt0
    invoke ExitProcess, 0

end start